#!/usr/bin/env python
# -*- coding: utf-8 -*-

###
# Wifi + GPS fingerprint collection tool
# Copyright (c) 2008, Yura Zenevich, Jorge Silva, and Jamon Camisso
# Homepage http://wiki.scyp.atrc.utoronto.ca/wiki/WiFiPositioning
# Contact scyp@atrc.utoronto.ca
#    
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program, see enclosed file gpl-3.txt or browse to
# <http://www.gnu.org/licenses/gpl.txt>
###

import wx
import string
import sqlite3

from Search import *
from WifiScanner import *
from WifiUtils import *
from StatsUtils import *
from FloorFinder import *
from numpy import *
from math import fabs
from math import sqrt

#	MAIN FUNCTION CALLED WITHIN WIFISCANNER.
#	RETURNS AVERAGE DISTANCE IN 2D SPACE OVER DISTANCE IN MAC 
#	SPACE, LIST OF DISTANCES FROM NEW POINT TO EXISTING CORRELATED 
#	POINTS IN MAC SPACE, LIST OF SCANTIME (SCAN IDS) THAT 
#	CORRELATE TO THE NEW SEARCHED LOCATION.
def WifiFind():

	#	PERFORM A NEW SCAN PROCEDURE THAT RETURNS A UNIQUE LIST OF MAC
	#	ADDRESSES AS WELL AS ORDERED SIGNAL STRENGTH VALUES FOR THESE
	#	MACS.
	scan = MultiScan(1) #Just one scan

	if len(scan[0]) == 0:
		raise ValueError, "Scan did not find any mac addresses."

	#	CREATE STANDARTIZED VECTOR THAT CONTAINS ALL THE DATA FOR A NEW
	#	SCAN: SCAN TIME, COORDINATES, LIST OF UNIQUE MAC ADDRESSES AND
	#	CORRESPONDING LIST OF THIER RANKED SIGNAL STRENGTH.
	newSearchdataVector = (None, (None, None), scan[0], scan[2])

	#	FIND ALL PREVIOUS SCANS THAT CONTAIN AT LEAST ONE MAC ADDRESS
	#	THAT WAS FOUND DURING THE NEW SCAN. EACH ELEMENT CONTAINS
	#	SCAN TIME, X AND Y COORDINATES OF THE SCAN.
	scans = ObtainScans(newSearchdataVector)

	#	DETERMINE WHAT FLOOR ARE WE ON AND FILTER PREVIOUS SCANS ACCORDINGLY.
	buildingName, floorNumber, scans = FloorFind(scan, scans)

	if len(scans) == 0:
		raise ValueError, "There are no related points to estimate."
	
	#	SEARCH THE DATABASE FOR ALL WIFI SCANS ASSOCIATED WITH EACH
	#	SCAN. CREATE A LIST OF STANDARTIZED VECTORS FOR EACH EXISTING
	#	SCAN THAT CONTAINS: SCAN TIME, COORDINATES, LIST OF UNIQUE MAC 
	#	ADDRESSES AND CORRESPONDING LIST OF THIER ORDERED SIGNAL 
	#	STRENGTH.
	dataVectors1 = GetDataVectors(scans)

	'''	JORGE'S METHOD		'''

	dataVectors2 = GetDataVectors2(dataVectors1)
	newSearchdataVector2 = GetNewSearchVector2(newSearchdataVector)

	'''	END OF JORGE'S METHOD	'''	

	#	BUILD A VECTOR (SO CALLED ABSOLUTE VECTOR) THAT CONTAINS ALL 
	#	MAC ADDRESSES SEEN EITHER IN THE NEW SCAN OR IN THE PREVIOUS
	#	REALATED SCANS.
	absoluteVector = BuildAbsoluteVector(dataVectors1, newSearchdataVector[2])

	#	UPDATE ALL STANDARTIZED DATA VECTORS IN A WAY THAT FOR EACH
	#	VECTOR A LIST OF MAC ADDRESSES AND THEIR SIGNAL STRENGTHS IS
	#	EXTENDED TO CONTAIN ALL MAC ADDRESSES FROM ABSOLUTE VECTOR 
	#	(THE ORDERED VALUE FOR THE SIGNAL STRENGTH FOR SUCH ADDITIONAL
	#	MAC ADDRESSES IS EQUAL TO 0).
	absolutifiedVectors = PopulateAbsoluteVectors(dataVectors1, absoluteVector)
	absolutifiedVectors2 = PopulateAbsoluteVectors(dataVectors2, absoluteVector)

	#	UPDATE NEW SEARCH DATA VECTOR THE SAME WAY AS ABOVE.
	absolutifiedNewSearch = Absolutify(newSearchdataVector, absoluteVector)
	absolutifiedNewSearch2 = Absolutify(newSearchdataVector2, absoluteVector)
	
	xMatrix = GetXMatrix(absolutifiedVectors)
	xMatrix2 = GetXMatrix(absolutifiedVectors2)
	xMatrixTranspose = xMatrix.T
	xMatrixTranspose2 = xMatrix2.T
	xTx = dot(xMatrixTranspose, xMatrix)
	xTx2 = dot(xMatrixTranspose2, xMatrix2)
	
	yVector = GetVector(absolutifiedVectors, 1)
	xVector = GetVector(absolutifiedVectors, 0)

	xTvY = dot(xMatrixTranspose, yVector)
	xTvX = dot(xMatrixTranspose, xVector)
	xTvY2 = dot(xMatrixTranspose2, yVector)
	xTvX2 = dot(xMatrixTranspose2, xVector)

	betax, betay = GetEstimators(xTx, xTvX, xTvY)
	betax2, betay2 = GetEstimators(xTx2, xTvX2, xTvY2)

	newVector = GetNewVector(absolutifiedNewSearch)
	newVector2 = GetNewVector(absolutifiedNewSearch2)
	
	xest, yest = Estimate(newVector, betax, betay)
	xest2, yest2 = Estimate(newVector2, betax2, betay2)

	sSqPredX, sSqPredY = SSqEst(xVector, yVector, betax, betay, xMatrixTranspose, xTvX, xTvY, newVector)
	sSqPredX2, sSqPredY2 = SSqEst(xVector, yVector, betax2, betay2, xMatrixTranspose2, xTvX2, xTvY2, newVector2)

	wx, wx2 = FindWeight(sSqPredX, sSqPredX2)
	wy, wy2 = FindWeight(sSqPredY, sSqPredY2)
	
	var1 = GetVar(dataVectors1)
	var2 = GetVar(dataVectors2)
	varCombined = (var1**(-1) + var2**(-1))**(-1)

	r1 = 2 * sqrt(var1)
	#r2 = 2 * sqrt(var2)
	r2=10
	rcomb = 2 * sqrt(varCombined)

	return (xest[0]*wx + xest2[0]*wx2, yest[0]*wy + yest2[0]*wy2), \
		   (xest[0], yest[0]), (xest2[0], yest2[0]), buildingName, \
		   floorNumber, r1, r2, rcomb
			

def GetVar(datav):
	SSE = 0
	for v in datav:
		newv = v
		datavtmp = datav[0:datav.index(v)] + datav[datav.index(v) + 1:len(datav)]
		
		av = BuildAbsoluteVector(datavtmp, v[2])
		avtmp = PopulateAbsoluteVectors(datavtmp, av)
		aNewSearch = Absolutify(newv, av)

		xMatrix = GetXMatrix(avtmp)
		xMatrixTranspose = xMatrix.T
		xTx = dot(xMatrixTranspose, xMatrix)
	
		yVector = GetVector(avtmp, 1)
		xVector = GetVector(avtmp, 0)

		xTvY = dot(xMatrixTranspose, yVector)
		xTvX = dot(xMatrixTranspose, xVector)

		betax, betay = GetEstimators(xTx, xTvX, xTvY)

		newVector = GetNewVector(aNewSearch)

		xest, yest = Estimate(newVector, betax, betay)
		
		SSE = SSE + max((xest - v[1][0])**2, (yest - v[1][1])**2)

	return SSE / fabs(len(xVector) - len(xMatrixTranspose))
		
		
		

#	UPDATE ALL STANDARTIZED DATA VECTORS IN A WAY THAT FOR EACH
#	VECTOR A LIST OF MAC ADDRESSES AND THEIR SIGNAL STRENGTHS IS
#	EXTENDED TO CONTAIN ALL MAC ADDRESSES FROM ABSOLUTE VECTOR 
#	(THE ORDERED VALUE FOR THE SIGNAL STRENGTH FOR SUCH ADDITIONAL
#	MAC ADDRESSES IS EQUAL TO 0).
def PopulateAbsoluteVectors(dataVectors, absoluteVector):
	
	avs = []
	
	for dataVector in dataVectors:
		avs.append(Absolutify(dataVector, absoluteVector))

	return avs

def GetNewSearchVector2(newSearchdataVector):

	oss = []

	for ss in newSearchdataVector[3]:
		oss.append(ss)

	oss.sort()
	newoss = []

	for ss in newSearchdataVector[3]:
		newoss.append( float(1 + oss.index(ss)) / len(oss))
	#endfor	

	return (newSearchdataVector[0], newSearchdataVector[1], newSearchdataVector[2], newoss)

def GetDataVectors2(dataVectors1):

	newdataVectors = []

	for v in dataVectors1:
		oss = []
		for ss in v[3]:
			oss.append(ss)
		#endfor
		oss.sort()
		newoss = []
		for ss in v[3]:
			newoss.append( float(1 + oss.index(ss)) / len(oss))
		#endfor
	#endfor
		
		newdataVectors.append((v[0], v[1], v[2], newoss))

	return newdataVectors

#	SEARCH THE DATABASE FOR ALL WIFI SCANS ASSOCIATED WITH EACH
#	SCAN. CREATE A LIST OF STANDARTIZED VECTORS FOR EACH EXISTING
#	SCAN THAT CONTAINS: SCAN TIME, COORDINATES, LIST OF UNIQUE MAC 
#	ADDRESSES AND CORRESPONDING LIST OF THIER ORDERED SIGNAL 
#	STRENGTH.
def GetDataVectors(scans):
	
	dataVectors = []

	for scan in scans:
		wifiScans = Search().FindWifiScan(scan[0])
		macs = wifiScans[0]
		orderedss = wifiScans[1]
		dataVectors.append((scan[0], (scan[1], scan[2]), macs, orderedss))

	return dataVectors

#	FIND ALL PREVIOUS SCANS THAT CONTAIN AT LEAST ONE MAC ADDRESS
#	THAT WAS FOUND DURING THE NEW SCAN. EACH ELEMENT CONTAINS
#	SCAN TIME, X AND Y COORDINATES OF THE SCAN.
def ObtainScans(newSearchdataVector):
	
	scans = []

	for mac in newSearchdataVector[2]:
		query = Search().FindScanInfo(mac)
		for scanTime in query[0]:
			scans.append((scanTime, query[1][query[0].index(scanTime)], query[2][query[0].index(scanTime)], query[3][query[0].index(scanTime)]))

	#	GET READ OF THE DUPLICATES IN THE SCANS LIST.
	return RemoveDuplicatesFromScans(scans)
